package kr.test.service;

import kr.test.exception.StorageException;
import kr.test.exception.StorageFileNotFoundException;
import kr.test.properties.StorageProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.util.FileSystemUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.stream.Stream;

@Service
public class FileSystemStroageService implements StorageService
{
    private final Path rootLocation;

    @Autowired
    public FileSystemStroageService(StorageProperties properties)
    {
        this.rootLocation = Paths.get(properties.getLocation());
    }

    @Override
    public void init()
    {
        try {
            Files.createDirectories(rootLocation);
        }
        catch (IOException e)
        {
            throw new StorageException("Could not initialize storage",e);
        }
    }

    @Override
    public void deleteAll()
    {
        FileSystemUtils.deleteRecursively(rootLocation.toFile());
    }

    @Override
    public Path load(String filename)
    {
        return rootLocation.resolve(filename);
    }

    @Override
    public Stream<Path> loadAll()
    {
        try
        {
            return Files.walk(rootLocation,1)
                    .filter(path -> !path.equals(rootLocation))
                    .map(rootLocation::relativize);
        }
        catch (IOException e)
        {
            throw new StorageException("Failed to read stored file.",e);
        }
    }

    @Override
    public Resource loadAsResource(String filename)
    {
        try {
            Path file = load(filename);
            Resource resource = new UrlResource(file.toUri());
            if(resource.exists() || resource.isReadable())
            {
                return resource;
            }
            else {
                throw new StorageFileNotFoundException("Could not read file: "+filename);
            }
        }
        catch (MalformedURLException e)
        {
            throw new StorageFileNotFoundException("Could not read file : "+filename,e);
        }
    }

    @Override
    public void store(MultipartFile file)
    {
        String filename = StringUtils.cleanPath(file.getOriginalFilename());
        try {
            if(file.isEmpty())
            {
                throw new StorageException("Failed to store empty file : "+filename);
            }
            if(filename.contains(".."))
            {
                throw new StorageException("Cannot store file with relative path outside current directory"+filename);
            }
            try(InputStream inputStream = file.getInputStream())
            {
                Files.copy(inputStream,rootLocation.resolve(filename), StandardCopyOption.REPLACE_EXISTING);
            }
        }
        catch (IOException e)
        {
            throw new StorageException("Failed to store file : "+ filename,e);
        }
    }
}
